/*
 * Copyright 2012 Alain Pannetrat (L1L1@gmx.com)
 * 
 * This file is part of cookie-miner.
 * 
 * cookie-miner is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * cookie-miner is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with cookie-miner. If not, see http://www.gnu.org/licenses/.
 *
 * @version: $Id$
 *
 */
#include "log_facility.h"
#include <ctime>
#include <pthread.h>
#include <string.h>

unsigned short next_thread_id = 0;

pthread_mutex_t thread_id_mutex = PTHREAD_MUTEX_INITIALIZER;

/**************************************
 * LOG_THREAD_INFO
 */

#define LOG_MAX 5

struct log_thread_info_t {
	unsigned short thread_id;
	unsigned short next_message;
	char *messages[LOG_MAX];
};

log_thread_info_t* log_thread_info_new()
{
	log_thread_info_t* ret = new log_thread_info_t;
	memset(ret,0,sizeof(log_thread_info_t));

	pthread_mutex_lock(&thread_id_mutex);
	ret->thread_id = next_thread_id++;
	pthread_mutex_unlock(&thread_id_mutex);

	return ret;
}

void log_thread_info_delete(void *lti)
{
	log_thread_info_t* info = (log_thread_info_t*)lti;
	for (unsigned i=0;i<LOG_MAX;i++)
	{
		if (info->messages[i])
			delete [] info->messages[i];
	}
	delete info;
}

char *log_thread_info_message_alloc(log_thread_info_t* info, unsigned size)
{
	char *r = new char[size];

	if (info->messages[info->next_message])
		delete [] info->messages[info->next_message];
	info->messages[info->next_message] = r;

	info->next_message = (info->next_message+1)%(LOG_MAX);

	return r;
}

/********************************************************************
 * LOG_FACILITY
 */

bool log_facility::_thread_key_registered =  false;

log_facility logger;

log_facility::log_facility(FILE *logfile) : _log(logfile), _level(0) 
{ 
	if (!_thread_key_registered)
	{
		pthread_key_create(&_thread_key,log_thread_info_delete);
		_thread_key_registered = true;
	}
	new_thread();
}

void log_facility::new_thread()
{
	pthread_setspecific(_thread_key,log_thread_info_new()); 
}

log_facility::~log_facility()
{
	pthread_key_delete(_thread_key);
}


char default_str[128];

log_facility& log_facility::message(unsigned level, const char *format, ...)
{ 
	int slen;
	int tlen;
	char *str = NULL;
	char logid[20];
	va_list va;
	log_thread_info_t* info;
	unsigned pid;

	if (_level<=level)
	{
		pid = getpid();
		info = (log_thread_info_t* )pthread_getspecific(_thread_key);

		if (info==NULL)
			sprintf(logid,"%04x.NULL",pid);
		else
			sprintf(logid,"%04x.%04x",pid,info->thread_id);

		slen = snprintf(str,0,"%s ",logid);
		va_start(va,format);
		tlen = vsnprintf(str,0,format,va);
		va_end(va);

		if (info==NULL)
			str = default_str;
		else
			str = log_thread_info_message_alloc(info,slen+tlen+2);

		sprintf(str,"%s ",logid);
		va_start(va,format);
		vsprintf(str+slen,format,va);
		va_end(va);

		if (str[slen+tlen-1]!='\n')
		{
			str[slen+tlen]='\n';
			tlen++;
		}
		write(STDERR_FILENO,str,slen+tlen);

		//delete [] str;
	}
	return *this;
}

